#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
SQL编辑器增强功能模块
提供代码折叠、多光标编辑、智能搜索替换等高级功能
"""

import re
import logging
from typing import List, Dict, Tuple, Optional
from dataclasses import dataclass

from PySide6.QtCore import Qt, Signal, QObject, QTimer, QRect, QPoint
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, 
                              QPushButton, QCheckBox, QLabel, QFrame,
                              QTextEdit, QScrollArea, QListWidget, QListWidgetItem)
from PySide6.QtGui import (QTextCursor, QTextCharFormat, QColor, QFont,
                          QKeyEvent, QPainter, QTextBlock, QTextDocument)

logger = logging.getLogger("enhanced_features")


@dataclass
class FoldRegion:
    """代码折叠区域"""
    start_line: int
    end_line: int
    folded: bool = False
    fold_type: str = "block"  # block, comment, query


@dataclass
class SearchResult:
    """搜索结果"""
    start_pos: int
    end_pos: int
    line_number: int
    text: str


class CodeFoldingManager(QObject):
    """代码折叠管理器"""
    
    fold_changed = Signal(int, bool)  # 行号, 是否折叠
    
    def __init__(self, editor):
        """初始化代码折叠管理器
        
        Args:
            editor: 文本编辑器
        """
        super().__init__()
        self.editor = editor
        self.fold_regions = {}  # 行号 -> FoldRegion
        self._update_timer = QTimer()
        self._update_timer.setSingleShot(True)
        self._update_timer.timeout.connect(self._update_fold_regions)
        
        # 连接编辑器信号
        self.editor.textChanged.connect(self._on_text_changed)
        
    def _on_text_changed(self):
        """文本改变时延迟更新折叠区域"""
        self._update_timer.start(500)  # 500ms后更新
        
    def _update_fold_regions(self):
        """更新可折叠区域"""
        document = self.editor.document()
        text = self.editor.toPlainText()
        
        # 清除旧的折叠区域
        old_regions = self.fold_regions.copy()
        self.fold_regions.clear()
        
        # 查找SQL查询块
        self._find_sql_blocks(text)
        
        # 查找注释块
        self._find_comment_blocks(text)
        
        # 查找括号块
        self._find_bracket_blocks(text)
        
        # 保持之前的折叠状态
        for line_num, region in self.fold_regions.items():
            if line_num in old_regions:
                region.folded = old_regions[line_num].folded
                
    def _find_sql_blocks(self, text: str):
        """查找SQL查询块"""
        lines = text.split('\n')
        current_block_start = None
        
        for i, line in enumerate(lines):
            stripped = line.strip().upper()
            
            # SQL查询开始
            if (stripped.startswith(('SELECT', 'WITH', 'INSERT', 'UPDATE', 'DELETE', 'CREATE')) and
                current_block_start is None):
                current_block_start = i
                
            # SQL查询结束（分号或空行）
            elif (stripped.endswith(';') or not stripped) and current_block_start is not None:
                if i > current_block_start + 2:  # 至少3行才可折叠
                    self.fold_regions[current_block_start] = FoldRegion(
                        start_line=current_block_start,
                        end_line=i,
                        fold_type="query"
                    )
                current_block_start = None
                
    def _find_comment_blocks(self, text: str):
        """查找注释块"""
        lines = text.split('\n')
        current_block_start = None
        
        for i, line in enumerate(lines):
            stripped = line.strip()
            
            # 多行注释开始
            if '/*' in stripped and current_block_start is None:
                current_block_start = i
                
            # 多行注释结束
            elif '*/' in stripped and current_block_start is not None:
                if i > current_block_start:
                    self.fold_regions[current_block_start] = FoldRegion(
                        start_line=current_block_start,
                        end_line=i,
                        fold_type="comment"
                    )
                current_block_start = None
                
            # 连续单行注释
            elif stripped.startswith('--'):
                if current_block_start is None:
                    current_block_start = i
            elif current_block_start is not None and not stripped.startswith('--'):
                if i > current_block_start + 1:  # 至少2行注释
                    self.fold_regions[current_block_start] = FoldRegion(
                        start_line=current_block_start,
                        end_line=i - 1,
                        fold_type="comment"
                    )
                current_block_start = None
                
    def _find_bracket_blocks(self, text: str):
        """查找括号块"""
        lines = text.split('\n')
        bracket_stack = []
        
        for i, line in enumerate(lines):
            for j, char in enumerate(line):
                if char == '(':
                    bracket_stack.append((i, j))
                elif char == ')' and bracket_stack:
                    start_line, start_col = bracket_stack.pop()
                    if i > start_line + 2:  # 至少跨越3行
                        self.fold_regions[start_line] = FoldRegion(
                            start_line=start_line,
                            end_line=i,
                            fold_type="block"
                        )
                        
    def toggle_fold(self, line_number: int) -> bool:
        """切换指定行的折叠状态
        
        Args:
            line_number: 行号
            
        Returns:
            是否成功切换
        """
        if line_number in self.fold_regions:
            region = self.fold_regions[line_number]
            region.folded = not region.folded
            self.fold_changed.emit(line_number, region.folded)
            return True
        return False
        
    def fold_all(self):
        """折叠所有区域"""
        for region in self.fold_regions.values():
            if not region.folded:
                region.folded = True
                self.fold_changed.emit(region.start_line, True)
                
    def unfold_all(self):
        """展开所有区域"""
        for region in self.fold_regions.values():
            if region.folded:
                region.folded = False
                self.fold_changed.emit(region.start_line, False)
                
    def is_line_foldable(self, line_number: int) -> bool:
        """检查行是否可折叠"""
        return line_number in self.fold_regions
        
    def is_line_folded(self, line_number: int) -> bool:
        """检查行是否已折叠"""
        if line_number in self.fold_regions:
            return self.fold_regions[line_number].folded
        return False


class MultiCursorManager(QObject):
    """多光标管理器"""
    
    cursors_changed = Signal()
    
    def __init__(self, editor):
        """初始化多光标管理器
        
        Args:
            editor: 文本编辑器
        """
        super().__init__()
        self.editor = editor
        self.extra_cursors = []  # 额外的光标位置
        self.selection_mode = False
        
    def add_cursor_at_position(self, position: int):
        """在指定位置添加光标
        
        Args:
            position: 光标位置
        """
        if position not in self.extra_cursors:
            self.extra_cursors.append(position)
            self.cursors_changed.emit()
            
    def add_cursor_at_line(self, line_number: int):
        """在指定行添加光标
        
        Args:
            line_number: 行号
        """
        document = self.editor.document()
        block = document.findBlockByLineNumber(line_number)
        if block.isValid():
            self.add_cursor_at_position(block.position())
            
    def remove_cursor_at_position(self, position: int):
        """移除指定位置的光标
        
        Args:
            position: 光标位置
        """
        if position in self.extra_cursors:
            self.extra_cursors.remove(position)
            self.cursors_changed.emit()
            
    def clear_extra_cursors(self):
        """清除所有额外光标"""
        if self.extra_cursors:
            self.extra_cursors.clear()
            self.cursors_changed.emit()
            
    def select_all_occurrences(self, text: str):
        """选择所有匹配的文本
        
        Args:
            text: 要匹配的文本
        """
        if not text:
            return
            
        document_text = self.editor.toPlainText()
        self.clear_extra_cursors()
        
        # 查找所有匹配位置
        start = 0
        while True:
            pos = document_text.find(text, start)
            if pos == -1:
                break
            self.add_cursor_at_position(pos)
            start = pos + 1
            
    def type_at_all_cursors(self, text: str):
        """在所有光标位置输入文本
        
        Args:
            text: 要输入的文本
        """
        if not self.extra_cursors:
            return
            
        cursor = self.editor.textCursor()
        cursor.beginEditBlock()
        
        # 从后往前处理，避免位置偏移
        positions = sorted(self.extra_cursors + [cursor.position()], reverse=True)
        
        for pos in positions:
            cursor.setPosition(pos)
            cursor.insertText(text)
            
        cursor.endEditBlock()
        
        # 更新光标位置
        text_length = len(text)
        self.extra_cursors = [pos + text_length for pos in self.extra_cursors]
        self.cursors_changed.emit()


class SmartSearchReplace(QWidget):
    """智能搜索替换面板"""
    
    search_requested = Signal(str, bool, bool)  # 搜索文本, 区分大小写, 正则表达式
    replace_requested = Signal(str, str, bool, bool)  # 搜索文本, 替换文本, 区分大小写, 正则表达式
    replace_all_requested = Signal(str, str, bool, bool)
    
    def __init__(self, parent=None):
        """初始化搜索替换面板"""
        super().__init__(parent)
        self.search_results = []
        self.current_result_index = -1
        self._init_ui()
        
    def _init_ui(self):
        """初始化用户界面"""
        layout = QVBoxLayout(self)
        layout.setContentsMargins(8, 8, 8, 8)
        layout.setSpacing(4)
        
        # 搜索行
        search_layout = QHBoxLayout()
        search_layout.addWidget(QLabel("查找:"))
        
        self.search_edit = QLineEdit()
        self.search_edit.setPlaceholderText("输入搜索内容...")
        self.search_edit.textChanged.connect(self._on_search_text_changed)
        self.search_edit.returnPressed.connect(self._search_next)
        search_layout.addWidget(self.search_edit)
        
        self.prev_btn = QPushButton("上一个")
        self.prev_btn.clicked.connect(self._search_previous)
        search_layout.addWidget(self.prev_btn)
        
        self.next_btn = QPushButton("下一个")
        self.next_btn.clicked.connect(self._search_next)
        search_layout.addWidget(self.next_btn)
        
        layout.addLayout(search_layout)
        
        # 替换行
        replace_layout = QHBoxLayout()
        replace_layout.addWidget(QLabel("替换:"))
        
        self.replace_edit = QLineEdit()
        self.replace_edit.setPlaceholderText("输入替换内容...")
        replace_layout.addWidget(self.replace_edit)
        
        self.replace_btn = QPushButton("替换")
        self.replace_btn.clicked.connect(self._replace_current)
        replace_layout.addWidget(self.replace_btn)
        
        self.replace_all_btn = QPushButton("全部替换")
        self.replace_all_btn.clicked.connect(self._replace_all)
        replace_layout.addWidget(self.replace_all_btn)
        
        layout.addLayout(replace_layout)
        
        # 选项行
        options_layout = QHBoxLayout()
        
        self.case_sensitive_cb = QCheckBox("区分大小写")
        options_layout.addWidget(self.case_sensitive_cb)
        
        self.regex_cb = QCheckBox("正则表达式")
        options_layout.addWidget(self.regex_cb)
        
        self.whole_word_cb = QCheckBox("全词匹配")
        options_layout.addWidget(self.whole_word_cb)
        
        options_layout.addStretch()
        
        # 结果计数标签
        self.result_label = QLabel("0 个结果")
        options_layout.addWidget(self.result_label)
        
        # 关闭按钮
        close_btn = QPushButton("×")
        close_btn.setFixedSize(24, 24)
        close_btn.clicked.connect(self.hide)
        options_layout.addWidget(close_btn)
        
        layout.addLayout(options_layout)
        
        # 设置样式
        self.setFrameStyle(QFrame.StyledPanel)
        self.setMaximumHeight(120)
        
    def _on_search_text_changed(self, text: str):
        """搜索文本改变时的处理"""
        if text:
            self._perform_search(text)
        else:
            self.search_results.clear()
            self.current_result_index = -1
            self._update_result_label()
            
    def _perform_search(self, text: str):
        """执行搜索"""
        case_sensitive = self.case_sensitive_cb.isChecked()
        is_regex = self.regex_cb.isChecked()
        whole_word = self.whole_word_cb.isChecked()
        
        self.search_requested.emit(text, case_sensitive, is_regex)
        
    def _search_next(self):
        """搜索下一个"""
        if self.search_results and self.current_result_index < len(self.search_results) - 1:
            self.current_result_index += 1
            self._highlight_current_result()
        elif self.search_results:
            self.current_result_index = 0
            self._highlight_current_result()
            
    def _search_previous(self):
        """搜索上一个"""
        if self.search_results and self.current_result_index > 0:
            self.current_result_index -= 1
            self._highlight_current_result()
        elif self.search_results:
            self.current_result_index = len(self.search_results) - 1
            self._highlight_current_result()
            
    def _replace_current(self):
        """替换当前匹配项"""
        if self.search_results and 0 <= self.current_result_index < len(self.search_results):
            search_text = self.search_edit.text()
            replace_text = self.replace_edit.text()
            case_sensitive = self.case_sensitive_cb.isChecked()
            is_regex = self.regex_cb.isChecked()
            
            self.replace_requested.emit(search_text, replace_text, case_sensitive, is_regex)
            
    def _replace_all(self):
        """替换所有匹配项"""
        search_text = self.search_edit.text()
        replace_text = self.replace_edit.text()
        case_sensitive = self.case_sensitive_cb.isChecked()
        is_regex = self.regex_cb.isChecked()
        
        self.replace_all_requested.emit(search_text, replace_text, case_sensitive, is_regex)
        
    def _highlight_current_result(self):
        """高亮当前搜索结果"""
        if self.search_results and 0 <= self.current_result_index < len(self.search_results):
            result = self.search_results[self.current_result_index]
            # 这里应该通知编辑器高亮指定位置
            # 具体实现需要与编辑器配合
            
    def set_search_results(self, results: List[SearchResult]):
        """设置搜索结果
        
        Args:
            results: 搜索结果列表
        """
        self.search_results = results
        self.current_result_index = 0 if results else -1
        self._update_result_label()
        
        if results:
            self._highlight_current_result()
            
    def _update_result_label(self):
        """更新结果计数标签"""
        if self.search_results:
            self.result_label.setText(f"{self.current_result_index + 1}/{len(self.search_results)} 个结果")
        else:
            self.result_label.setText("0 个结果")
            
    def focus_search(self):
        """聚焦到搜索框"""
        self.search_edit.setFocus()
        self.search_edit.selectAll()


class CodeMinimap(QWidget):
    """代码缩略图"""
    
    def __init__(self, editor, parent=None):
        """初始化代码缩略图
        
        Args:
            editor: 文本编辑器
            parent: 父窗口
        """
        super().__init__(parent)
        self.editor = editor
        self.setFixedWidth(120)
        self.setMinimumHeight(200)
        
        # 连接编辑器信号
        self.editor.textChanged.connect(self.update)
        self.editor.verticalScrollBar().valueChanged.connect(self.update)
        
    def paintEvent(self, event):
        """绘制缩略图"""
        painter = QPainter(self)
        painter.fillRect(self.rect(), QColor(240, 240, 240))
        
        # 获取编辑器文档
        document = self.editor.document()
        if not document:
            return
            
        # 计算缩放比例
        total_lines = document.blockCount()
        if total_lines == 0:
            return
            
        widget_height = self.height()
        line_height = max(1, widget_height // total_lines)
        
        # 绘制代码行
        painter.setFont(QFont("Consolas", 2))
        
        block = document.firstBlock()
        y = 0
        
        while block.isValid() and y < widget_height:
            text = block.text().strip()
            if text:
                # 根据代码类型设置颜色
                if text.upper().startswith(('SELECT', 'FROM', 'WHERE')):
                    painter.setPen(QColor(0, 0, 255))  # 蓝色 - SQL关键字
                elif text.startswith('--') or text.startswith('/*'):
                    painter.setPen(QColor(0, 128, 0))  # 绿色 - 注释
                else:
                    painter.setPen(QColor(0, 0, 0))    # 黑色 - 普通代码
                    
                # 绘制简化的代码行
                painter.drawLine(2, y, min(len(text) * 2, self.width() - 4), y)
                
            y += line_height
            block = block.next()
            
        # 绘制可视区域指示器
        scrollbar = self.editor.verticalScrollBar()
        if scrollbar.maximum() > 0:
            visible_ratio = scrollbar.pageStep() / (scrollbar.maximum() + scrollbar.pageStep())
            scroll_ratio = scrollbar.value() / scrollbar.maximum()
            
            indicator_height = max(10, int(widget_height * visible_ratio))
            indicator_y = int((widget_height - indicator_height) * scroll_ratio)
            
            painter.fillRect(0, indicator_y, self.width(), indicator_height, 
                           QColor(100, 100, 100, 100))
            
    def mousePressEvent(self, event):
        """鼠标点击事件 - 跳转到对应位置"""
        if event.button() == Qt.LeftButton:
            # 计算点击位置对应的行号
            click_ratio = event.pos().y() / self.height()
            
            scrollbar = self.editor.verticalScrollBar()
            target_value = int(click_ratio * scrollbar.maximum())
            scrollbar.setValue(target_value)


class BracketMatcher(QObject):
    """括号匹配器"""
    
    bracket_matched = Signal(int, int)  # 开始位置, 结束位置
    
    def __init__(self, editor):
        """初始化括号匹配器
        
        Args:
            editor: 文本编辑器
        """
        super().__init__()
        self.editor = editor
        self.bracket_pairs = {'(': ')', '[': ']', '{': '}'}
        self.current_matches = []
        
        # 连接编辑器信号
        self.editor.cursorPositionChanged.connect(self._on_cursor_changed)
        
    def _on_cursor_changed(self):
        """光标位置改变时检查括号匹配"""
        cursor = self.editor.textCursor()
        position = cursor.position()
        text = self.editor.toPlainText()
        
        # 清除之前的匹配
        self.current_matches.clear()
        
        if position > 0 and position <= len(text):
            # 检查光标前的字符
            char_before = text[position - 1] if position > 0 else ''
            char_at = text[position] if position < len(text) else ''
            
            match_pos = -1
            if char_before in self.bracket_pairs:
                match_pos = self._find_matching_bracket(text, position - 1, char_before, True)
                if match_pos != -1:
                    self.current_matches = [position - 1, match_pos]
                    self.bracket_matched.emit(position - 1, match_pos)
            elif char_at in self.bracket_pairs.values():
                for open_br, close_br in self.bracket_pairs.items():
                    if char_at == close_br:
                        match_pos = self._find_matching_bracket(text, position, char_at, False)
                        if match_pos != -1:
                            self.current_matches = [match_pos, position]
                            self.bracket_matched.emit(match_pos, position)
                        break
                        
    def _find_matching_bracket(self, text: str, start_pos: int, bracket: str, forward: bool) -> int:
        """查找匹配的括号
        
        Args:
            text: 文本内容
            start_pos: 开始位置
            bracket: 括号字符
            forward: 是否向前查找
            
        Returns:
            匹配括号的位置，-1表示未找到
        """
        if forward:
            target = self.bracket_pairs.get(bracket)
            if not target:
                return -1
                
            count = 1
            for i in range(start_pos + 1, len(text)):
                if text[i] == bracket:
                    count += 1
                elif text[i] == target:
                    count -= 1
                    if count == 0:
                        return i
        else:
            # 反向查找
            for open_br, close_br in self.bracket_pairs.items():
                if bracket == close_br:
                    count = 1
                    for i in range(start_pos - 1, -1, -1):
                        if text[i] == bracket:
                            count += 1
                        elif text[i] == open_br:
                            count -= 1
                            if count == 0:
                                return i
                    break
                    
        return -1 